Pro Entity Framework Core 2 for ASP.NET Core MVC 翻译

第 2 章 第一个 Entity Framework Core 应用程序

作者:Adam Freeman
翻译:陈广
日期:2018-11-24


开始 Entity Framework Core 的最佳方式是跳进去并使用它。本章我将使用 Entity Framework Core 和 ASP.NET Core MVC 创建一个简单的应用程序,以便您看到所有东西是如何组合在一起的。为保持示例的简单,我将跳过一些将在后面章节介绍的细节。

准备工作

为准备本章以及后面章节中的示例,您需要安装一些开发工具。ASP.NET Core MVC 和 Entity Framework Core 开发所需的所有工具都是零成本版本,我在整本书中都使用了这些版本。


本书的更新 Microsoft 对 .NET Core、ASP.NET Core MVC、和 Entity Framework Core, 有一个积极的开发计划,这意味着您在阅读本书时可能已经有新的版本。期待您每隔几个月买一本新书似乎不太合适,尤其是大多数变化都相对较小。

相反,我将在这本书的 GitHub 存储库上发布免费更新(https://github.com/apress/pro-ef-core-2-for-ASP.NET-core-mvc),以打破由小版本引起的更改。

这类更新对于我及 Apress 是一个尝试,还不知道这些更新会采取什么形式 —— 尤其是因为我不知道 ASP.NET Core MVC 或 Entity Framework Core 未来的主要版本将包含哪些内容 —— 但目的是通过补充本书所包含的例子来延长这本书的寿命。

我不打算做任何承诺,更新将是什么样的,他们将采取什么形式,或多久之后我会把他们打包成这本书一个新版本。当新的 ASP.NET Core MVC 版本发布时,请保持开放的心态,并检查本书的存储库。如果你对如何改进更新有自己的想法,那就给我发电子邮件:adam@adam-freeman.com,然后告诉我。


安装 .NET Core

.NET Core 软件开发工具包(SDK)包含创建和运行 .NET 项目所需的运行时和开发工具。要在 Windows 下安装 .NET Core SDK,请从https://www.microsoft.com/net/download/thank-you/dotnet-sdk-2.1.4-windows-x64-installer 下载安装程序。此 URL 是 64 位 .NET Core SDK 2.14 版本,这是我在整本书中使用的版本,您应该安装它,以确保从示例中获得预期的结果。(微软还发布了一个仅用于运行时的安装程序,但这并不包含本书所需的工具。)

运行安装程序,一旦完成安装进程,打开一个新的 PowerShell 窗口或命令提示符并运行清单 2-1 所示的命令以检查 .NET Core 是否正常工作。

清单 2-1:测试 .NET Core

dotnet --version

此命令的输出将显示安装的 .NET Core 运行时的最新版本。如果您只安装了之前指定的版本,则为 2.1.4。

安装 Visual Studio 2017

Visual Studio 是 ASP.NET Core 和 Entity Framework Core 项目的传统开发环境。请从https://www.visualstudio.com/vs 下载安装程序。Visual Studio 2017 分为几个不同版本,但免费的社区版已经满足本书示例的要求。运行安装程序并确保选择了【.NET Core 跨平台开发】工作负载,如图2-1所示。

图2-1 选择 Visual Studio 安装包

此工作负载包括整本书所使用的 SQL Server 的 LocalDB 版本,以及 ASP.NET Core MVC 和 Entity Framework Core 开发所需的 Visual Studio 功能。单击【安装】按钮开始下载和安装 Visual Studio 功能的过程。

添加 Visual Studio 扩展

对于 ASP.NET Core MVC 项目,两个 Visual Studio 扩展是必不可少的。第一个名为【Razor language Services】,它在编辑 Razor 视图时提供了对标签助手的智能感知支持。第二个叫【Project File Tools】,它在编辑 .csproj 文件时提供自动完成功能,这简化了将 Nuget 包添加到项目中的过程。(您可能会发现,这些扩展已经安装,因为 Microsoft 会不时更改默认添加的扩展。)

译者注:在翻译此文时,我使用的是 Visual Studio 2017 15.9.2 版本,其中第一个扩展已经默认安装。

在 Visual Studio 【工具】菜单中选择【扩展和更新】,选择【联机】部分,并使用搜索框查找扩展,单击【下载】按钮以下载扩展文件,如图2-2所示。

图2-2 下载 Visual Studio 扩展

单击【关闭】按钮以关闭扩展列表,然后关闭 Visual Studio,这将触发您下载的扩展的安装过程。系统将提示您接受将要进行的更改和许可条款,如图2-3所示。单击【修改】按钮以安装扩展。一旦流程完成,您可以启动 Visual Studio 并开始工作。

图2-3 安装 Visual Studio 扩展

创建项目

为开始 Entity Framework Core,我将展示如何创建一个简单的数据输入应用程序,并将其数据存储在数据库中。我将快速地完成这个过程,避免过多的细节,只让您了解 ASP.NET Core MVC 和 Entity Framework Core 是如何协同工作的。不用担心,我在本章中所做的每一件事都会在后面的章节中作深入的解释。

场景设定

想象一下,一个朋友决定举办一个新年晚会,她让我创建一个 web 应用程序,让她的受邀者以电子方式回复。她要求提供以下四个关键功能:

  • 显示聚会信息的主页
  • 可用于 RSVP(回复) 的表单
  • RSVP 表单验证,它将显示一个感谢页面
  • 显示谁来参加聚会的摘要页

这与我在《Pro ASP.NET MVC Core 2》这本书中创建的的数据输入应用程序相同,本书的不同之处在于,我将使用 Entity Framework Core 将响应存储在数据库中。

创建项目

要创建项目,请启动 Visual Studio 并在【文件】菜单中选择【新建】➤【项目】。选择【ASP.NET Core Web 应用程序】项目模板,将【名称】项设置为PartyInvites,并单击【浏览】按钮选择一个方便的位置存储项目,如图2-4所示。

图2-4 创建示例项目

单击【确定】按钮继续项目设置。确保窗体顶部的【.NET Core】和【ASP.NET Core 2.1】被选中,并单击【空】模板,如图2-5所示。Visual Studio包含在项目中设置 ASP.NET Core MVC 和 Entity Framework Core 的模板,但是结果隐藏了一些有用的细节。

图2-5 配置 ASP.NET Core 项目

单击【确定】按钮,Visual Studio 将使用基本配置创建 PartyInvites 项目,该配置设置 ASP.NET Core,但不配置 MVC framework 或 Entity Framework Core。

添加 Bootstrap CSS 框架

我在整本书中使用 Bootstrap CSS 框架对 HTML 元素进行样式化。我在 PartyInvites 项目中单击鼠标右键,在弹出菜单中选择【添加】➤【添加客户端库】,并将 twitter-bootstrap 添加至项目中。最终生成的 libman.json 配置文件代码清单18-7所示:

清单 2-2:PartyInvites 文件夹下的 libman.json 文件的内容

{
  "version": "1.0",
  "defaultProvider": "cdnjs",
  "libraries": [
    {
      "library": "twitter-bootstrap@4.1.3",
      "destination": "wwwroot/lib/twitter-bootstrap/"
    }
  ]
}

配置 HTTP 端口

更改 ASP.NET Core 使用接收请求的端口来将使示例更容易理解。编辑 Properties 文件夹下的 launchSettings.json 文件,并如清单2-4所示修改 URL。

清单 2-4:Properties 文件夹下的 launchSettings.json 文件,更改 HTTP 端口

{
  "iisSettings": {
    "windowsAuthentication": false, 
    "anonymousAuthentication": true, 
    "iisExpress": {
      "applicationUrl": "http://localhost:5000", //此处更改
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "PartyInvites": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5000",//此处更改
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

该文件中的 URL 用于在应用程序使用 IIS Express 启动和从命令行运行时配置该应用程序。本书中的所有示例都是从命令行运行的,这样就可以很容易地看到日志记录消息。

创建数据模型和 Context 类

当您创建同时使用 ASP.NET Core MVC 和 Entity Framework Core 的应用程序时,数据模型变得尤为重要。要为示例应用程序创建数据模型类,我在项目中添加了 Models 文件夹,并在其中创建了一个 GuestResponse.cs 文件,添加如清单2-5所示的代码:

清单 2-5:Models 文件夹下的 GuestResponse.cs 文件的内容

namespace PartyInvites.Models
{
    public class GuestResponse
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
        public bool? WillAttend { get; set; }
    }
}

Entity Framework Core 能够存储普通 C# 类实例,只要它们有一个可以唯一标识每个对象的属性(被称为主键属性)。在GuestResponse类中,Id属性就是主键属性。

Entity Framework Core 特性由数据库 context 类提供,该类将数据模型类标识为 Entity Framework Core,并用于访问数据库中的数据。要创建 context 类,我在 Models 文件夹下添加了 DataContext.cs 文件,并在其中添加清单2-6所示的代码。

清单 2-6:Models 文件夹下的 DataContext.cs 文件的内容

using Microsoft.EntityFrameworkCore;

namespace PartyInvites.Models
{
    public class DataContext : DbContext
    {
        public DataContext(DbContextOptions<DataContext> options)
            : base(options) { }

        public DbSet<GuestResponse> Responses { get; set; }
    }
}

在创建了一个数据库 context 类时,最重要的是包含一个接收配置对象的构造器,以及将其传递给基类构造器。对于每个您想访问的数据模型类,context 类定义了一个返回DbSet<T>对象的属性,数据正是通过此对象进行存储和检索的。我定义了一个返回DbSet<GuestResponse>对象的属性,所以可以存储并检索GuestResponse对象。

创建控制器和视图

为给应用程序提供控制器,我创建了 Controllers 文件夹并向其中添加了一个名为 HomeController.cs 的文件,代码如清单2-7所示。

清单 2-7:Controllers 文件夹下的 HomeController.cs 文件的内容

using Microsoft.AspNetCore.Mvc;
using PartyInvites.Models;
using System.Linq;

namespace PartyInvites.Controllers
{
    public class HomeController : Controller
    {
        private DataContext context;

        public HomeController(DataContext ctx) => context = ctx;

        public IActionResult Index() => View();

        public IActionResult Respond() => View();

        [HttpPost]
        public IActionResult Respond(GuestResponse response)
        {
            context.Responses.Add(response);
            context.SaveChanges();
            return RedirectToAction(nameof(Thanks),
                new { Name = response.Name, WillAttend = response.WillAttend });
        }

        public IActionResult Thanks(GuestResponse response) => View(response);

        public IActionResult ListResponses() =>
            View(context.Responses.OrderByDescending(r => r.WillAttend));
    }
}

控制器接收一个DataContext对象作为构造器参数,并使用它访问 Entity Framework Core 管理的数据。由数据库 context 类的Responses属性返回的DbSet<GuestResponse>对象实现了IEnumerable<GuestResponse>接口,并将在枚举时自动查询数据库以获取存储的GuestResponse对象。

DbSet<GuestResponse>对象还用于存储对象。Add方法用于为 Entity Framework Core 提供要存储的对象,SaveChanges方法执行更新。

提示:在更为复杂的项目中,我推荐使用存储库模式来访问 Entity Framework Core 功能,如第10章所述。对于本章的简单项目,控制器直接工作于数据库 context 类。

我创建了 View/Home 文件夹并向其添加了一个名为 _Layout.cshtml 的文件,其内容如清单2-8所示。此布局将为本章中的所有其它视图提供公共布局,并使用一个link元素包含 Bootstrap CSS 文件。

清单 2-8:Views/Home 文件夹下的 _Layout.cshtml 文件的内容

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Party Invites</title>
    <link rel="stylesheet" href="~/lib/twitter-bootstrap/css/bootstrap.css"/>
</head>
<body>
    <div>
        @RenderBody()
    </div>
</body>
</html>

为了在应用程序的视图中默认使用布局,我在 Views 文件夹中添加了一个名为 _ViewStart.cshtml 的文件,内容如清单2-9所示。

清单 2-9:Veiws 文件夹下的 _ViewStart.cshtml 文件的内容

@{
    Layout = "_Layout";
}

为创建提供登录页面的视图,我在 Views/Home 文件夹下添加了一个名为 Index.cshtml 的文件,其内容如清单2-10所示。

清单 2-10:Views/Home 文件夹下的 Index.cshtml 文件的内容

<div class="text-center m-4">
    <h3>We're going to have an exciting party!</h3>
    <h4>And you are invited</h4>
    <a class="btn btn-primary" asp-action="Respond">RSVP Now</a>
</div>

为从用户那接收回复的详细信息,我在 Views/Home 文件夹下添加了一个名为 Respond.cshtml 的文件,其内容如清单2-11所示。

清单 2-11:View/Home 文件夹下的 Respond.cshtml 文件的内容

@model GuestResponse

<div class="bg-primary p-2 text-white text-center">
    <h2>RSVP</h2>
</div>

<form asp-action="Respond" method="post" class="m-4">
    <div class="form-group">
        <label>Your Name</label>
        <input asp-for="Name" class="form-control" />
    </div>
    <div class="form-group">
        <label>Your Email</label>
        <input asp-for="Email" class="form-control" />
    </div>
    <div class="form-group">
        <label>Your Phone Number</label>
        <input asp-for="Phone" class="form-control" />
    </div>
    <div class="form-group">
        <label>Will You Attend?</label>
        <select asp-for="WillAttend" class="form-control">
            <option value="">Choose an option</option>
            <option value="true">Yes, I'll be there</option>
            <option value="false">No, I can't come</option>
        </select>
    </div>
    <div class="text-center">
        <button type="submit" class="btn btn-primary">Submit RSVP</button>
    </div>
</form>

为确认由用户提供的回复,我在 Views/Home 文件夹中添加了一个名为 Thanks.cshtml 的文件,其内容如清单2-12所示。

清单 2-12:Views/Home 文件夹下的 Thanks.cshtml 文件的内容

@model GuestResponse

<div class="text-center mt-3">
    <h1>Thank you, @Model.Name!</h1>
    @if (Model.WillAttend == true)
    {
        <div>
            It's great that you're coming. The drinks are already in the fridge!
        </div>
    }
    else
    {
        <div>
            Sorry to hear that you can't make it, but thanks for letting us know.
        </div>
    }
    Click <a asp-action="ListResponses">here</a> to see who is coming.
</div>

下面创建最后一个视图,我在 Views/Home 文件夹下添加了一个名为 ListResponses.cshtml 的文件,其内容如清单2-13所示,它将列出应用程序所接收到的所有回复。

清单 2-13:Views/Home 文件夹下的 ListResponses.cshtml 文件的内容

@model IEnumerable<GuestResponse>

<h3 class="bg-primary p-2 text-white text-center">
    Here is the list of people who have
    responded
</h3>
<div class="container-fluid">
    <div class="row p-1">
        <div class="col font-weight-bold">Name</div>
        <div class="col font-weight-bold">Email</div>
        <div class="col font-weight-bold">Phone</div>
        <div class="col font-weight-bold">Attending</div>
    </div>
    @foreach (GuestResponse r in Model)
    {
        <div class="row p-1">
            <div class="col">@r.Name</div>
            <div class="col">@r.Email</div>
            <div class="col">@r.Phone</div>
            <div class="col">@(r.WillAttend == true ? "Yes" : "No")</div>
        </div>
    }
</div>

为完成应用程序视图的配置,我在 Views 文件夹下添加了一个名为 _ViewImports.cshtml 的文件,内容如清单2-14所示。

清单 2-14:Views 文件夹下的 _ViewImports.cshtml 文件的内容

@using PartyInvites.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

这些语句允许在视图中不受限地使用 Models 命名空间中的类,并激活标签助手功能,它是我在视图中配置一些 HTML 元素所要依赖的。

配置 Entity Framework Core

当 Visual Studio 创建了一个新的 ASP.NET Core 项目时,它添加了几乎所有 ASP.NET Core MVC 和 Entity Framework Core 开发所需的 NuGet 包。还需要添加对命令行工具的支持,它是 Entity Framework Core 用来准备数据库存储数据的工具。在【解决方案资源管理器】中右键单击 PartyInvites 项目,在弹出菜单中选择【编辑 PartyInvites.csproj】,并添加如清单2-15所示的元素。

清单 2-15:PartyInvites 文件夹下的 PartyInvites.csproj 文件,添加程序包

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" PrivateAssets="All" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.3" />
  </ItemGroup>

</Project>

译者注:本书写作时,作者使用的是 .NET Core 2.0,而我在翻译此文章时,使用的是 .NET Core 2.1。此时,.csproj 文件已经有了一些改变, 所以此处会跟原文有所不同。另外,Visual Studio 15.8 之后的版本已经内置 Microsoft.EntityFrameworkCore.Tools.DotNet,所以上面这个步骤可以省略。

提供命令行工具的程序包必须使用DotNetCliToolReference元素进行添加。Visual Studio 提供了管理 NuGet 包的工具,但无法添加此类程序包。当你保存文件,Visual Studio 将下载程序包并添加至项目。

配置连接字符串

当使用数据库时,必须提供一个连接字符串,它告诉 Entity Framework Core 如何连接到数据库,并且通常包括其他信息,例如身份验证凭据。在【解决方案资源管理器】中右键单击 PartyInvites 项目,在弹出按钮中选择【添加】➤【新建项】,并选择【应用设置文件】模板,请确保名称设置为 appsettings.json,如图2-6所示。

图2-6 创建一个应用程序配置文件

单击【添加】按钮创建文件,并按清单2-16所示更改连接字符串。

清单 2-16:PartyInvites 文件夹下的 appsettings.json 文件,定义连接字符串

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=PartyInvites"
  }
}

此连接字符串告诉 Entity Framework Core 在使用一个名为 PartyInvites 的本地数据库,本地数据库(LocalDB)会随 Visual Studio 一起安装。

配置 Startup 类

下一步是通过向Startup类添加如清单2-17所示的语句来配置应用程序以设置 ASP.NET Core MVC 和 Entity Framework Core。

清单 2-17:PartyInvites 文件夹下的 Startup.cs 文件,配置应用程序

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using PartyInvites.Models;

namespace PartyInvites
{
    public class Startup
    {
        public Startup(IConfiguration config) => Configuration = config;

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            string conString = Configuration["ConnectionStrings:DefaultConnection"];
            services.AddDbContext<DataContext>(options =>
                options.UseSqlServer(conString));
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseDeveloperExceptionPage();
            app.UseStatusCodePages();
            app.UseStaticFiles();
            app.UseMvcWithDefaultRoute();
        }
    }
}

这些更改加载 appsettings.json 文件中的设置,并使用它们配置数据库 context 类,以便 Entity Framework Core 使用它,并通过依赖注入功能将其作为服务使用,从而允许应用程序的其他部分轻松获得 context 对象并访问 Entity Framework Core 功能。

准备数据库

Entity Framework Core 已经创建和配置了数据库,所以它可以用于存储GuestResponse对象。这需要通过创建一个*迁移(migration)*来实现。Entity Framework Core 检索应用程序的数据模型并确定如何将其存储在关系数据库中。迁移的结果包含一组指令,由数据库提供程序类转换为 SQL 命令,以告诉数据库服务器创建 Entity Framework Core 所需的数据库。我将在第12章详细解释迁移是如何工作的。

迁移通过命令行工具创建和应用。打开一个新的 PowerShell 窗口或命令提示符,导航到 PartyInvites 项目文件夹(包含 libman.json 和 Startup.cs 文件的文件夹),并运行清单 2-18 所示的命令。

清单 2-18:创建一个新的迁移

dotnet ef migrations add Initial

要应用迁移并创建数据库,请在 PartyInvites 文件夹中运行清单2-19所示的命令。 清单 2-19:应用迁移

dotnet ef database update

测试应用程序

要启动应用程序,请在 PartyInvites 项目文件夹下运行清单2-20所示的命令。虽然您可以使用 Visual Studio 启动应用程序,但本书中的许多示例都依赖于应用程序生成的日志消息,当应用程序从命令行启动时,这些信息更容易看到;这是我在接下来的所有章节中采用的方法。

清单 2-20:启动应用程序

dotnet run

应用程序启动后,打开新的浏览器窗口并导航到 http://localhost:5000,您将看到如图2-7所示的内容。

图2-7 运行示例应用程序

单击【RSVP Now】按钮,填充表单,并单击【Submit RSVP】按钮。您将看到如图2-8所示的响应。

图2-8 创建一个响应

单击链接以查看响应列表。您将看到与图2-9类似的内容,它显示了我创建的其它响应。

图2-9 显示响应列表

MVC 模型绑定从 HTTP POST 请求中的值所创建的GuestResponse对象存储在数据库中。当您请求响应列表时,将查询数据库中的数据,Entity Framework Core 使用查询结果创建一系列用于生成列表的GuestResponse对象。

总结

在本章中,我创建了一个新的 ASP.NET Core 项目,并使用它创建一个简单的数据输入应用程序,该应用程序使用 Entity Framework Core 将其数据存储在 LocalDB 数据库中。您看到了将 Entity Framework Core 添加到 MVC 应用程序是多么容易,以及持久地存储数据所需的更改是多么的少,至少对于一个简单的应用程序是如此的。在下一章中,我将概述 Visual Studio 为处理数据库提供的工具,并使用它们解释 Entity Framework Core 所依赖的最重要的 SQL 命令。

;

© 2018 - IOT小分队文章发布系统 v0.3